package common

import (
	"context"
	"errors"
	"log"
	"strings"
	"time"

	"github.com/google/uuid"

	"github.com/bsm/redislock"

	"github.com/redis/go-redis/v9"
)

var rdb *redis.Client

func init() {
	rdb = redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
		//Password: "",
		DB:           0,
		PoolSize:     10,
		MinIdleConns: 5,
	})
	ctx := context.Background()
	if _, err := rdb.Ping(ctx).Result(); err != nil {
		log.Fatalln("redis ping err:", err)
	}
}

func RedisDB() *redis.Client {
	return rdb
}

func GetRedisCache(key string, expiration time.Duration, doSome func() (any, error)) (any, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()
	result, err := rdb.Get(ctx, key).Result()
	if errors.Is(err, redis.Nil) {
		locker := redislock.New(rdb)
		token := uuid.New().String()
		options := &redislock.Options{
			RetryStrategy: redislock.LinearBackoff(1 * time.Second),
			Token:         token,
		}
		lock, err := locker.Obtain(ctx, strings.Join([]string{key, "lock"}, "-"), 15*time.Second, options)
		if err != nil {
			return nil, err
		}
		defer func(lock *redislock.Lock, ctx context.Context) {
			if lock.Token() == token {
				err := lock.Release(ctx)
				if err != nil {
					panic(err)
				}
			}
		}(lock, ctx)
		// 获取分布式锁之后
		result, err := rdb.Get(ctx, key).Result()
		if errors.Is(err, redis.Nil) {
			data, err := doSome()
			if err != nil {
				return nil, err
			}
			err = rdb.Set(ctx, key, data, expiration).Err()
			if err != nil {
				return nil, err
			}
			return data, nil
		} else if err != nil {
			return nil, err
		}
		return result, nil
	} else if err != nil {
		return nil, err
	}
	return result, nil
}

func SetRedisCache(key string, expiration time.Duration, doSome func() (any, error)) error {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()
	err := rdb.GetEx(ctx, key, 10*time.Second).Err()
	if !errors.Is(err, redis.Nil) && err != nil {
		return err
	}
	data, err := doSome()
	if err != nil {
		return err
	}
	err = rdb.Set(ctx, key, data, expiration).Err()
	if err != nil {
		return err
	}
	return nil
}
